﻿using System;
using System.Linq;
using System.Linq.Expressions;
using nExt.Core.Extensions;
using nExt.Core.Helper;

namespace nExt.Core
{
	/// <summary>
	/// Statische Klasse um bestimmte vorraussetzungen zu Prüfen
	/// </summary>
	public static class Check
	{
		private static readonly string argumentNullExceptionMessage = "Parameter '{0}' in Method '{1}' is null" + Environment.NewLine + "Method '{1}' was called from '{2}'";

		/// <summary>
		/// Prüft ob die Bedingung <paramref name="condition"/> erfüllt ist. 
		/// </summary>
		/// <param name="condition">Bedingung die erfüllt sein muss, sonst wird eine Ausnahme geworfen</param>
		/// <param name="exceptionCreateFactory">Methode um die exception zu erzeugen, die geworfen werden soll, wenn condition false ist</param>
		public static void Requires(bool condition, Func<Exception> exceptionCreateFactory )
		{
			if (!condition)
				throw exceptionCreateFactory();
		}

		/// <summary>
		/// Prüft ob die Bedingung <paramref name="condition"/> erfüllt ist. 
		/// Wenn nicht wird eine Ausnahme vom <typeparamref name="TException"/> geworfen.
		/// </summary>
		/// <typeparam name="TException">Typ der Ausnahme die geworfen werden soll</typeparam>
		/// <param name="condition">Bedingung die erfüllt sein muss, sonst wird eine Ausnahme geworfen</param>
		public static void Requires<TException>(bool condition)
			where TException : Exception, new()
		{
			if (!condition)
				throw new TException();
		}

		/// <summary>
		/// Prüft ob die Bedingung <paramref name="condition" /> erfüllt ist.
		/// Wenn nicht wird eine Ausnahme vom <typeparamref name="TException" /> geworfen.
		/// </summary>
		/// <typeparam name="TException">Typ der Ausnahme die geworfen werden soll</typeparam>
		/// <param name="condition">Bedingung die erfüllt sein muss, sonst wird eine Ausnahme geworfen</param>
		/// <param name="message">Fehlermedlung</param>
		public static void Requires<TException>(bool condition, string message)
			where TException : Exception, new()
		{
			if (!condition)
				throw (TException)Activator.CreateInstance(typeof(TException), message);
		}

		/// <summary>
		/// Prüft ob einer der <c>parameter</c> null ist.
		/// </summary>
		/// <exception cref="System.ArgumentNullException">Falls <code>parameter</code> null ist.</exception>
		public static void NotNull(Expression<Func<object>> expression1, Expression<Func<object>> expression2,
			params Expression<Func<object>>[] parameters)
		{
			foreach (var parameterName in from expression in parameters.Concat(new[] { expression1, expression2 })
										  let parameterToCheck = expression.Compile()() 
										  where parameterToCheck == null 
										  select expression.GetMemberName())
				throw new ArgumentNullException(parameterName, string.Format(argumentNullExceptionMessage, parameterName, ReflectionHelper.GetCallingMethod().Name, ReflectionHelper.GetCallingMethod(1).Name));
		}

		/// <summary>
		/// Prüft ob ein <c>parameter</c> null ist.
		/// </summary>
		/// <exception cref="System.ArgumentNullException">Falls <code>parameter</code> null ist.</exception>
		/// <param name="parameter">Ausdruck des zu prüfenden Objektes z.B ()=>name</param>
		public static T NotNull<T>(Expression<Func<T>> parameter)
		{
			var parameterToCheck = parameter.Compile()();
			if (parameterToCheck == null)
			{
				var parameterName = parameter.GetMemberName();
				throw new ArgumentNullException(parameterName, string.Format(argumentNullExceptionMessage, parameterName, ReflectionHelper.GetCallingMethod().Name, ReflectionHelper.GetCallingMethod(1).Name));
			}
			return parameterToCheck;
		}

		/// <summary>
		/// Prüft ob ein <c>parameter</c> null ist.
		/// </summary>
		/// <exception cref="System.ArgumentNullException">Falls <code>parameter</code> null ist.</exception>
		/// <param name="parameter">Das zu prüfende Objekt</param>
		/// <param name="parameterName">Name des Parameters</param>
		public static void NotNull(object parameter, string parameterName)
		{
			if (parameter == null)
				throw new ArgumentNullException(parameterName, string.Format(argumentNullExceptionMessage, parameterName, ReflectionHelper.GetCallingMethod().Name, ReflectionHelper.GetCallingMethod(1).Name));
		}

		/// <summary>
		/// Prüft ob ein <see cref="Guid"/> is leer (Empty)
		/// </summary>
		/// <param name="parameter">The parameter.</param>
		/// <param name="parameterName">Name of the parameter.</param>
		public static void NotNull(Guid parameter, string parameterName)
		{
			if (parameter == Guid.Empty)
				throw new ArgumentNullException(parameterName, string.Format(argumentNullExceptionMessage, parameterName, ReflectionHelper.GetCallingMethod().Name, ReflectionHelper.GetCallingMethod(1).Name));
		}

		/// <summary>
		/// Prüft ob ein <c>parameter</c> null or leer ist.
		/// </summary>
		/// <param name="parameter">The parameter.</param>
		/// <param name="parameterName">Name of the parameter.</param>
		public static void NotNullOrEmpty(string parameter, string parameterName)
		{
			if (String.IsNullOrEmpty(parameter))
				throw new ArgumentNullException(string.Format(argumentNullExceptionMessage, parameterName, ReflectionHelper.GetCallingMethod().Name, ReflectionHelper.GetCallingMethod(1).Name));
		}


		/// <summary>
		/// Prüft ob das Betribssystem mindestens Vista ist
		/// </summary>
		public static void IsVistaOrHigher()
		{
			if (!(Environment.OSVersion.Version.Major >= 6))
				throw new InvalidOperationException("Invalid OperatingSystem");
		}
	}
}